Refactor program loader to handle many loadable segments
The original implementation of program loader is written under the assumption that there are only two loadable segments per ELF, one is code, and the other is data. But this assumption is unnecessary and proves to be wrong for an ELF on Alpine Linux, which has two extra read-only, loadable segments for security hardening. This commit clears the obstacle towards running unmodified executables from Alpine Linux. In addition to getting rid of the false assumption of two fixed loadable segments, this commit improves the quality of the code related to program loading and process initialization.
This commit is contained in:
parent
1a56fc4b72
commit
c8a921fd4b
@ -84,7 +84,7 @@ $(LIBOS_CORE_A): $(LIBOS_RS_A) $(C_OBJS) $(S_OBJS) $(EDL_C_OBJS)
|
|||||||
@ar r $@ $(C_OBJS) $(S_OBJS) $(EDL_C_OBJS)
|
@ar r $@ $(C_OBJS) $(S_OBJS) $(EDL_C_OBJS)
|
||||||
@echo "GEN => $@"
|
@echo "GEN => $@"
|
||||||
|
|
||||||
ifeq ($(RELEASE), 0)
|
ifeq ($(LIBOS_RELEASE), 0)
|
||||||
$(LIBOS_RS_A): $(RUST_SRCS)
|
$(LIBOS_RS_A): $(RUST_SRCS)
|
||||||
@RUSTC_BOOTSTRAP=1 cargo build --target-dir=$(RUST_TARGET_DIR) -Z unstable-options --out-dir=$(RUST_OUT_DIR)
|
@RUSTC_BOOTSTRAP=1 cargo build --target-dir=$(RUST_TARGET_DIR) -Z unstable-options --out-dir=$(RUST_OUT_DIR)
|
||||||
@echo "CARGO (debug) => $@"
|
@echo "CARGO (debug) => $@"
|
||||||
|
@ -95,7 +95,8 @@ fn do_boot(program_path: &PathBuf, argv: &Vec<CString>) -> Result<()> {
|
|||||||
let envp = &config::LIBOS_CONFIG.env;
|
let envp = &config::LIBOS_CONFIG.env;
|
||||||
let file_actions = Vec::new();
|
let file_actions = Vec::new();
|
||||||
let parent = &process::IDLE_PROCESS;
|
let parent = &process::IDLE_PROCESS;
|
||||||
process::do_spawn(&program_path, argv, envp, &file_actions, parent)?;
|
let program_path_str = program_path.to_str().unwrap();
|
||||||
|
process::do_spawn(&program_path_str, argv, envp, &file_actions, parent)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#![feature(allocator_api)]
|
#![feature(allocator_api)]
|
||||||
#![feature(range_contains)]
|
#![feature(range_contains)]
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
|
#![feature(stmt_expr_attributes)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@ -47,6 +48,7 @@ use error::Result;
|
|||||||
mod prelude;
|
mod prelude;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod entry;
|
mod entry;
|
||||||
mod exception;
|
mod exception;
|
||||||
|
@ -4,7 +4,7 @@ pub use self::futex::{futex_op_and_flags_from_u32, futex_wait, futex_wake, Futex
|
|||||||
pub use self::process::{Status, IDLE_PROCESS};
|
pub use self::process::{Status, IDLE_PROCESS};
|
||||||
pub use self::process_table::get;
|
pub use self::process_table::get;
|
||||||
pub use self::sched::{do_sched_getaffinity, do_sched_setaffinity, CpuSet};
|
pub use self::sched::{do_sched_getaffinity, do_sched_setaffinity, CpuSet};
|
||||||
pub use self::spawn::{do_spawn, FileAction};
|
pub use self::spawn::{do_spawn, ElfFile, FileAction, ProgramHeaderExt};
|
||||||
pub use self::task::{current_pid, get_current, run_task};
|
pub use self::task::{current_pid, get_current, run_task};
|
||||||
pub use self::thread::{do_clone, do_set_tid_address, CloneFlags, ThreadGroup};
|
pub use self::thread::{do_clone, do_set_tid_address, CloneFlags, ThreadGroup};
|
||||||
pub use self::wait::{WaitQueue, Waiter};
|
pub use self::wait::{WaitQueue, Waiter};
|
||||||
|
82
src/libos/src/process/spawn/elf_file.rs
Normal file
82
src/libos/src/process/spawn/elf_file.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
use xmas_elf::symbol_table::Entry;
|
||||||
|
use xmas_elf::{header, program, sections};
|
||||||
|
|
||||||
|
pub use xmas_elf::header::HeaderPt2 as ElfHeader;
|
||||||
|
pub use xmas_elf::program::{ProgramHeader, ProgramIter};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ElfFile<'a> {
|
||||||
|
elf_buf: &'a [u8],
|
||||||
|
elf_inner: xmas_elf::ElfFile<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ElfFile<'a> {
|
||||||
|
pub fn new(elf_buf: &'a [u8]) -> Result<ElfFile> {
|
||||||
|
let elf_inner =
|
||||||
|
xmas_elf::ElfFile::new(elf_buf).map_err(|e| errno!(ENOEXEC, "invalid ELF header"))?;
|
||||||
|
Self::validate(&elf_inner)?;
|
||||||
|
|
||||||
|
Ok(ElfFile { elf_buf, elf_inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program_headers<'b>(&'b self) -> ProgramIter<'b, 'a> {
|
||||||
|
self.elf_inner.program_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elf_header(&self) -> &ElfHeader {
|
||||||
|
&self.elf_inner.header.pt2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_slice(&self) -> &[u8] {
|
||||||
|
self.elf_buf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(elf_inner: &xmas_elf::ElfFile) -> Result<()> {
|
||||||
|
// Validate the ELF header
|
||||||
|
xmas_elf::header::sanity_check(elf_inner)
|
||||||
|
.map_err(|e| errno!(ENOEXEC, "invalid ELF header"))?;
|
||||||
|
// Validate the segments
|
||||||
|
for segment in elf_inner.program_iter() {
|
||||||
|
segment.validate()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ProgramHeaderExt {
|
||||||
|
fn loadable(&self) -> bool;
|
||||||
|
fn validate(&self) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ProgramHeaderExt for ProgramHeader<'a> {
|
||||||
|
/// Is the segment loadable?
|
||||||
|
fn loadable(&self) -> bool {
|
||||||
|
let type_ = self.get_type().unwrap();
|
||||||
|
type_ == xmas_elf::program::Type::Load
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do some basic sanity checks in case the ELF is corrupted somehow
|
||||||
|
fn validate(&self) -> Result<()> {
|
||||||
|
let ph64 = match self {
|
||||||
|
ProgramHeader::Ph32(ph) => {
|
||||||
|
return_errno!(ENOEXEC, "not support 32-bit ELF");
|
||||||
|
}
|
||||||
|
ProgramHeader::Ph64(ph64) => ph64,
|
||||||
|
};
|
||||||
|
if !ph64.align.is_power_of_two() {
|
||||||
|
return_errno!(EINVAL, "invalid memory alignment");
|
||||||
|
}
|
||||||
|
if (ph64.offset % ph64.align) != (ph64.virtual_addr % ph64.align) {
|
||||||
|
return_errno!(
|
||||||
|
EINVAL,
|
||||||
|
"memory address and file offset is not equal, per modulo"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ph64.mem_size < ph64.file_size {
|
||||||
|
return_errno!(EINVAL, "memory size must be no less than file size");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,140 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
use xmas_elf::program::ProgramHeader;
|
|
||||||
use xmas_elf::sections::Rela;
|
|
||||||
use xmas_elf::symbol_table::Entry;
|
|
||||||
use xmas_elf::symbol_table::{DynEntry64, Entry64};
|
|
||||||
use xmas_elf::{program, sections, ElfFile, P64};
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Copy, Debug)]
|
|
||||||
pub struct ProgramHeaderInfo {
|
|
||||||
pub addr: usize,
|
|
||||||
pub entry_size: usize,
|
|
||||||
pub entry_num: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_program_header_info(elf_file: &ElfFile) -> Result<ProgramHeaderInfo> {
|
|
||||||
let elf_header = &elf_file.header.pt2;
|
|
||||||
Ok(ProgramHeaderInfo {
|
|
||||||
addr: elf_header.ph_offset() as usize,
|
|
||||||
entry_size: elf_header.ph_entry_size() as usize,
|
|
||||||
entry_num: elf_header.ph_count() as usize,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_program_headers(elf_file: &ElfFile) -> Result<()> {
|
|
||||||
println!("Program headers:");
|
|
||||||
let ph_iter = elf_file.program_iter();
|
|
||||||
for sect in ph_iter {
|
|
||||||
program::sanity_check(sect, &elf_file)
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "sanity check for program header failed"))?;
|
|
||||||
println!("\t{:?}", sect.get_type());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_sections(elf_file: &ElfFile) -> Result<()> {
|
|
||||||
println!("Sections:");
|
|
||||||
let mut sect_iter = elf_file.section_iter();
|
|
||||||
sect_iter.next(); // Skip the first, dummy section
|
|
||||||
for sect in sect_iter {
|
|
||||||
sections::sanity_check(sect, &elf_file)
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "sanity check for program header failed"))?;
|
|
||||||
let sec_name = sect
|
|
||||||
.get_name(&elf_file)
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to get section name"))?;
|
|
||||||
println!("\t{}\n{:?}", sec_name, sect);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_rela_plt_section(elf_file: &ElfFile) -> Result<()> {
|
|
||||||
let rela_entries = get_rela_entries(elf_file, ".rela.plt")
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to get .pltrel entries"))?;
|
|
||||||
let dynsym_entries = get_dynsym_entries(elf_file)
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to get .dynsym entries"))?;
|
|
||||||
|
|
||||||
println!(".rela.plt section:");
|
|
||||||
for entry in rela_entries {
|
|
||||||
println!(
|
|
||||||
"\toffset: {}, symbol index: {}, type: {}, addend: {}",
|
|
||||||
entry.get_offset(),
|
|
||||||
entry.get_symbol_table_index(),
|
|
||||||
entry.get_type(),
|
|
||||||
entry.get_addend()
|
|
||||||
);
|
|
||||||
|
|
||||||
let symidx = entry.get_symbol_table_index() as usize;
|
|
||||||
let dynsym_entry = &dynsym_entries[symidx];
|
|
||||||
let dynsym_name = dynsym_entry
|
|
||||||
.get_name(&elf_file)
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to get the name of a dynamic symbol"))?;
|
|
||||||
println!("\t\t{} = {:?}", dynsym_name, dynsym_entry);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data_program_header<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result<ProgramHeader<'a>> {
|
|
||||||
let mut ph_iter = elf_file.program_iter();
|
|
||||||
ph_iter
|
|
||||||
.find(|&ph| {
|
|
||||||
ph.get_type() == Ok(program::Type::Load)
|
|
||||||
&& !ph.flags().is_execute()
|
|
||||||
&& ph.flags().is_write()
|
|
||||||
&& ph.flags().is_read()
|
|
||||||
})
|
|
||||||
.ok_or_else(|| errno!(ENOEXEC, "failed to get the data segment"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_code_program_header<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result<ProgramHeader<'a>> {
|
|
||||||
let mut ph_iter = elf_file.program_iter();
|
|
||||||
ph_iter
|
|
||||||
.find(|&ph| {
|
|
||||||
ph.get_type() == Ok(program::Type::Load)
|
|
||||||
&& ph.flags().is_execute()
|
|
||||||
&& !ph.flags().is_write()
|
|
||||||
&& ph.flags().is_read()
|
|
||||||
})
|
|
||||||
.ok_or_else(|| errno!(ENOEXEC, "failed to get the code segment"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_start_address<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result<usize> {
|
|
||||||
let elf_header = &elf_file.header.pt2;
|
|
||||||
Ok(elf_header.entry_point() as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_sym_entries<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result<&'a [Entry64]> {
|
|
||||||
elf_file
|
|
||||||
.find_section_by_name(".symtab")
|
|
||||||
.and_then(|symtab_section| symtab_section.get_data(&elf_file).ok())
|
|
||||||
.and_then(|symbol_table| match symbol_table {
|
|
||||||
sections::SectionData::SymbolTable64(entries) => Some(entries),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.ok_or_else(|| errno!(ENOEXEC, "failed get the symbol entries"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_rela_entries<'b, 'a: 'b>(
|
|
||||||
elf_file: &'b ElfFile<'a>,
|
|
||||||
sec_name: &'b str,
|
|
||||||
) -> Result<&'a [Rela<P64>]> {
|
|
||||||
elf_file
|
|
||||||
.find_section_by_name(sec_name)
|
|
||||||
.and_then(|plt_rela_section| plt_rela_section.get_data(&elf_file).ok())
|
|
||||||
.and_then(|rela_table| match rela_table {
|
|
||||||
sections::SectionData::Rela64(entries) => Some(entries),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.ok_or_else(|| errno!(ENOEXEC, "failed to get .rela.plt entries"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_dynsym_entries<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result<&'a [DynEntry64]> {
|
|
||||||
elf_file
|
|
||||||
.find_section_by_name(".dynsym")
|
|
||||||
.and_then(|dynamic_section| dynamic_section.get_data(&elf_file).ok())
|
|
||||||
.and_then(|dynamic_table| match dynamic_table {
|
|
||||||
sections::SectionData::DynSymbolTable64(entries) => Some(entries),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.ok_or_else(|| errno!(ENOEXEC, "failed to get .dynsym entries"))
|
|
||||||
}
|
|
@ -1,73 +1,31 @@
|
|||||||
use self::segment::*;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use xmas_elf::{header, program, sections, ElfFile};
|
|
||||||
|
|
||||||
pub fn do_init(
|
pub fn do_init<'a, 'b>(
|
||||||
elf_file: &ElfFile,
|
elf_file: &'b ElfFile<'a>,
|
||||||
elf_buf: &[u8],
|
ldso_elf_file: &'b ElfFile<'a>,
|
||||||
ldso_elf_file: &ElfFile,
|
|
||||||
ldso_elf_buf: &[u8],
|
|
||||||
) -> Result<ProcessVM> {
|
) -> Result<ProcessVM> {
|
||||||
// Alloc all virtual memory areas
|
let mut process_vm = ProcessVMBuilder::new(vec![elf_file, ldso_elf_file])
|
||||||
let mut code_seg = get_code_segment(elf_file)?;
|
|
||||||
let mut data_seg = get_data_segment(elf_file)?;
|
|
||||||
let code_start = 0;
|
|
||||||
let code_end = align_down(data_seg.get_mem_addr(), data_seg.get_mem_align());
|
|
||||||
let data_start = code_end;
|
|
||||||
let data_end = align_up(data_seg.get_mem_addr() + data_seg.get_mem_size(), 4096);
|
|
||||||
let code_size = code_end - code_start;
|
|
||||||
let data_size = data_end - data_start;
|
|
||||||
|
|
||||||
let mut ldso_code_seg = get_code_segment(ldso_elf_file)?;
|
|
||||||
let mut ldso_data_seg = get_data_segment(ldso_elf_file)?;
|
|
||||||
let ldso_code_start = 0;
|
|
||||||
let ldso_code_end = align_down(ldso_data_seg.get_mem_addr(), ldso_data_seg.get_mem_align());
|
|
||||||
let ldso_data_start = ldso_code_end;
|
|
||||||
let ldso_data_end = align_up(
|
|
||||||
ldso_data_seg.get_mem_addr() + ldso_data_seg.get_mem_size(),
|
|
||||||
4096,
|
|
||||||
);
|
|
||||||
let ldso_code_size = ldso_code_end - ldso_code_start;
|
|
||||||
let ldso_data_size = ldso_data_end - ldso_data_start;
|
|
||||||
|
|
||||||
let mut process_vm = ProcessVMBuilder::new(code_size, data_size)
|
|
||||||
.ldso_code_size(ldso_code_size)
|
|
||||||
.ldso_data_size(ldso_data_size)
|
|
||||||
.build()
|
.build()
|
||||||
.cause_err(|e| errno!(e.errno(), "failed to create process VM"))?;
|
.cause_err(|e| errno!(e.errno(), "failed to create process VM"))?;
|
||||||
|
|
||||||
// Load code and data
|
|
||||||
let process_base_addr = process_vm.get_code_range().start();
|
|
||||||
code_seg.set_runtime_base(process_base_addr);
|
|
||||||
data_seg.set_runtime_base(process_base_addr);
|
|
||||||
code_seg.load_from_file(elf_buf);
|
|
||||||
data_seg.load_from_file(elf_buf);
|
|
||||||
|
|
||||||
// Load code and data of ld.so
|
|
||||||
let ldso_base_addr = process_vm.get_ldso_code_range().start();
|
|
||||||
ldso_code_seg.set_runtime_base(ldso_base_addr);
|
|
||||||
ldso_data_seg.set_runtime_base(ldso_base_addr);
|
|
||||||
ldso_code_seg.load_from_file(ldso_elf_buf);
|
|
||||||
ldso_data_seg.load_from_file(ldso_elf_buf);
|
|
||||||
|
|
||||||
// Relocate symbols
|
// Relocate symbols
|
||||||
//reloc_symbols(process_base_addr, elf_file)?;
|
//reloc_symbols(process_base_addr, elf_file)?;
|
||||||
//link_syscalls(process_base_addr, elf_file)?;
|
//link_syscalls(process_base_addr, elf_file)?;
|
||||||
|
|
||||||
Ok(process_vm)
|
Ok(process_vm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
fn reloc_symbols(process_base_addr: usize, elf_file: &ElfFile) -> Result<()> {
|
fn reloc_symbols(process_base_addr: usize, elf_file: &ElfFile) -> Result<()> {
|
||||||
let rela_entries = elf_helper::get_rela_entries(elf_file, ".rela.dyn")?;
|
let rela_entries = elf_helper::get_rela_entries(elf_file, ".rela.dyn")?;
|
||||||
for rela_entry in rela_entries {
|
for rela_entry in rela_entries {
|
||||||
/*
|
println!(
|
||||||
println!("\toffset: {:#X}, symbol index: {}, type: {}, addend: {:#X}",
|
"\toffset: {:#X}, symbol index: {}, type: {}, addend: {:#X}",
|
||||||
rela_entry.get_offset(),
|
rela_entry.get_offset(),
|
||||||
rela_entry.get_symbol_table_index(),
|
rela_entry.get_symbol_table_index(),
|
||||||
rela_entry.get_type(),
|
rela_entry.get_type(),
|
||||||
rela_entry.get_addend());
|
rela_entry.get_addend()
|
||||||
*/
|
);
|
||||||
|
|
||||||
match rela_entry.get_type() {
|
match rela_entry.get_type() {
|
||||||
// reloc type == R_X86_64_RELATIVE
|
// reloc type == R_X86_64_RELATIVE
|
||||||
@ -84,7 +42,7 @@ fn reloc_symbols(process_base_addr: usize, elf_file: &ElfFile) -> Result<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
fn link_syscalls(process_base_addr: usize, elf_file: &ElfFile) -> Result<()> {
|
fn link_syscalls(process_base_addr: usize, elf_file: &ElfFile) -> Result<()> {
|
||||||
let syscall_addr = __occlum_syscall as *const () as usize;
|
let syscall_addr = __occlum_syscall as *const () as usize;
|
||||||
|
|
||||||
@ -107,7 +65,8 @@ fn link_syscalls(process_base_addr: usize, elf_file: &ElfFile) -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn __occlum_syscall(num: i32, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64) -> i64;
|
fn __occlum_syscall(num: i32, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64) -> i64;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
use xmas_elf::symbol_table::Entry;
|
use super::*;
|
||||||
use xmas_elf::{header, program, sections, ElfFile};
|
|
||||||
|
|
||||||
use fs::{File, FileDesc, FileTable, INodeExt, OpenFlags, StdinFile, StdoutFile, ROOT_INODE};
|
|
||||||
use misc::ResourceLimitsRef;
|
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sgxfs::SgxFile;
|
use std::sgxfs::SgxFile;
|
||||||
use vm::{ProcessVM, ProcessVMBuilder};
|
|
||||||
|
|
||||||
use super::task::Task;
|
use super::fs::{
|
||||||
use super::*;
|
File, FileDesc, FileTable, INodeExt, OpenFlags, StdinFile, StdoutFile, ROOT_INODE,
|
||||||
|
};
|
||||||
|
use super::misc::ResourceLimitsRef;
|
||||||
|
use super::vm::{ProcessVM, ProcessVMBuilder};
|
||||||
|
|
||||||
|
pub use self::elf_file::{ElfFile, ProgramHeaderExt};
|
||||||
use self::init_stack::{AuxKey, AuxTable};
|
use self::init_stack::{AuxKey, AuxTable};
|
||||||
|
|
||||||
mod elf_helper;
|
mod elf_file;
|
||||||
mod init_stack;
|
mod init_stack;
|
||||||
mod init_vm;
|
mod init_vm;
|
||||||
mod segment;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FileAction {
|
pub enum FileAction {
|
||||||
@ -32,73 +31,34 @@ pub enum FileAction {
|
|||||||
Close(FileDesc),
|
Close(FileDesc),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_spawn<P: AsRef<Path>>(
|
pub fn do_spawn(
|
||||||
elf_path: &P,
|
elf_path: &str,
|
||||||
argv: &[CString],
|
argv: &[CString],
|
||||||
envp: &[CString],
|
envp: &[CString],
|
||||||
file_actions: &[FileAction],
|
file_actions: &[FileAction],
|
||||||
parent_ref: &ProcessRef,
|
parent_ref: &ProcessRef,
|
||||||
) -> Result<u32> {
|
) -> Result<u32> {
|
||||||
let mut elf_buf = {
|
let elf_buf = load_elf_to_vec(elf_path, parent_ref)
|
||||||
let path = elf_path.as_ref().to_str().unwrap();
|
.cause_err(|e| errno!(e.errno(), "cannot load the executable"))?;
|
||||||
let inode = parent_ref
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.lookup_inode(path)
|
|
||||||
.map_err(|e| errno!(e.errno(), "cannot find the executable"))?;
|
|
||||||
inode
|
|
||||||
.read_as_vec()
|
|
||||||
.map_err(|e| errno!(e.errno(), "failed to read the executable ELF"))?
|
|
||||||
};
|
|
||||||
let elf_file = {
|
|
||||||
let elf_file = ElfFile::new(&elf_buf)
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to parse the executable ELF"))?;
|
|
||||||
header::sanity_check(&elf_file)
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to parse the executable ELF"))?;
|
|
||||||
/*
|
|
||||||
elf_helper::print_program_headers(&elf_file)?;
|
|
||||||
elf_helper::print_sections(&elf_file)?;
|
|
||||||
elf_helper::print_pltrel_section(&elf_file)?;
|
|
||||||
*/
|
|
||||||
elf_file
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ldso_elf_buf = {
|
|
||||||
let ldso_path = "/lib/ld-musl-x86_64.so.1";
|
let ldso_path = "/lib/ld-musl-x86_64.so.1";
|
||||||
let ldso_inode = ROOT_INODE.lookup(ldso_path).map_err(|e| {
|
let ldso_elf_buf = load_elf_to_vec(ldso_path, parent_ref)
|
||||||
errno!(
|
.cause_err(|e| errno!(e.errno(), "cannot load ld.so"))?;
|
||||||
e.errno(),
|
|
||||||
"cannot find the loader at /lib/ld-musl-x86_64.so.1"
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
ldso_inode
|
|
||||||
.read_as_vec()
|
|
||||||
.map_err(|e| errno!(e.errno(), "failed to read the ld.so ELF"))?
|
|
||||||
};
|
|
||||||
|
|
||||||
let ldso_elf_file = {
|
let exec_elf_file =
|
||||||
let ldso_elf_file = ElfFile::new(&ldso_elf_buf)
|
ElfFile::new(&elf_buf).cause_err(|e| errno!(e.errno(), "invalid executable"))?;
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to parse the ld.so ELF"))?;
|
let ldso_elf_file =
|
||||||
header::sanity_check(&ldso_elf_file)
|
ElfFile::new(&ldso_elf_buf).cause_err(|e| errno!(e.errno(), "invalid ld.so"))?;
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to parse the ld.so ELF"))?;
|
|
||||||
/*
|
|
||||||
elf_helper::print_program_headers(&elf_file)?;
|
|
||||||
elf_helper::print_sections(&elf_file)?;
|
|
||||||
elf_helper::print_pltrel_section(&elf_file)?;
|
|
||||||
*/
|
|
||||||
ldso_elf_file
|
|
||||||
};
|
|
||||||
|
|
||||||
let (new_pid, new_process_ref) = {
|
let (new_pid, new_process_ref) = {
|
||||||
let cwd = parent_ref.lock().unwrap().get_cwd().to_owned();
|
let cwd = parent_ref.lock().unwrap().get_cwd().to_owned();
|
||||||
let vm = init_vm::do_init(&elf_file, &elf_buf[..], &ldso_elf_file, &ldso_elf_buf[..])?;
|
let vm = init_vm::do_init(&exec_elf_file, &ldso_elf_file)?;
|
||||||
let base_addr = vm.get_base_addr();
|
let auxtbl = init_auxtbl(&vm, &exec_elf_file)?;
|
||||||
let auxtbl = init_auxtbl(&vm, &elf_file)?;
|
|
||||||
let task = {
|
let task = {
|
||||||
let ldso_entry = {
|
let ldso_entry = {
|
||||||
let ldso_base_addr = vm.get_ldso_code_range().start();
|
let ldso_range = vm.get_elf_ranges()[1];
|
||||||
let ldso_entry = ldso_base_addr + elf_helper::get_start_address(&ldso_elf_file)?;
|
let ldso_entry =
|
||||||
if !vm.get_ldso_code_range().contains(ldso_entry) {
|
ldso_range.start() + ldso_elf_file.elf_header().entry_point() as usize;
|
||||||
|
if !ldso_range.contains(ldso_entry) {
|
||||||
return_errno!(EINVAL, "Invalid program entry");
|
return_errno!(EINVAL, "Invalid program entry");
|
||||||
}
|
}
|
||||||
ldso_entry
|
ldso_entry
|
||||||
@ -130,6 +90,17 @@ pub fn do_spawn<P: AsRef<Path>>(
|
|||||||
Ok(new_pid)
|
Ok(new_pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_elf_to_vec(elf_path: &str, parent_ref: &ProcessRef) -> Result<Vec<u8>> {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
parent_ref
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.lookup_inode(elf_path)
|
||||||
|
.map_err(|e| errno!(e.errno(), "cannot find the ELF"))?
|
||||||
|
.read_as_vec()
|
||||||
|
.map_err(|e| errno!(e.errno(), "failed to read the executable ELF"))
|
||||||
|
}
|
||||||
|
|
||||||
fn init_files(parent_ref: &ProcessRef, file_actions: &[FileAction]) -> Result<FileTable> {
|
fn init_files(parent_ref: &ProcessRef, file_actions: &[FileAction]) -> Result<FileTable> {
|
||||||
// Usually, we just inherit the file table from the parent
|
// Usually, we just inherit the file table from the parent
|
||||||
let parent = parent_ref.lock().unwrap();
|
let parent = parent_ref.lock().unwrap();
|
||||||
@ -183,7 +154,7 @@ fn init_files(parent_ref: &ProcessRef, file_actions: &[FileAction]) -> Result<Fi
|
|||||||
Ok(file_table)
|
Ok(file_table)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_auxtbl(process_vm: &ProcessVM, elf_file: &ElfFile) -> Result<AuxTable> {
|
fn init_auxtbl(process_vm: &ProcessVM, exec_elf_file: &ElfFile) -> Result<AuxTable> {
|
||||||
let mut auxtbl = AuxTable::new();
|
let mut auxtbl = AuxTable::new();
|
||||||
auxtbl.set(AuxKey::AT_PAGESZ, 4096)?;
|
auxtbl.set(AuxKey::AT_PAGESZ, 4096)?;
|
||||||
auxtbl.set(AuxKey::AT_UID, 0)?;
|
auxtbl.set(AuxKey::AT_UID, 0)?;
|
||||||
@ -191,20 +162,20 @@ fn init_auxtbl(process_vm: &ProcessVM, elf_file: &ElfFile) -> Result<AuxTable> {
|
|||||||
auxtbl.set(AuxKey::AT_EUID, 0)?;
|
auxtbl.set(AuxKey::AT_EUID, 0)?;
|
||||||
auxtbl.set(AuxKey::AT_EGID, 0)?;
|
auxtbl.set(AuxKey::AT_EGID, 0)?;
|
||||||
auxtbl.set(AuxKey::AT_SECURE, 0)?;
|
auxtbl.set(AuxKey::AT_SECURE, 0)?;
|
||||||
|
auxtbl.set(AuxKey::AT_SYSINFO, 0)?;
|
||||||
|
|
||||||
let process_base_addr = process_vm.get_process_range().start();
|
let exec_elf_base = process_vm.get_elf_ranges()[0].start() as u64;
|
||||||
let ph = elf_helper::get_program_header_info(elf_file)?;
|
let exec_elf_header = exec_elf_file.elf_header();
|
||||||
auxtbl.set(AuxKey::AT_PHDR, (process_base_addr + ph.addr) as u64)?;
|
auxtbl.set(AuxKey::AT_PHENT, exec_elf_header.ph_entry_size() as u64)?;
|
||||||
auxtbl.set(AuxKey::AT_PHENT, ph.entry_size as u64)?;
|
auxtbl.set(AuxKey::AT_PHNUM, exec_elf_header.ph_count() as u64)?;
|
||||||
auxtbl.set(AuxKey::AT_PHNUM, ph.entry_num as u64)?;
|
auxtbl.set(AuxKey::AT_PHDR, exec_elf_base + exec_elf_header.ph_offset())?;
|
||||||
|
auxtbl.set(
|
||||||
|
AuxKey::AT_ENTRY,
|
||||||
|
exec_elf_base + exec_elf_header.entry_point(),
|
||||||
|
)?;
|
||||||
|
|
||||||
let program_entry = process_base_addr + elf_helper::get_start_address(&elf_file)?;
|
let ldso_elf_base = process_vm.get_elf_ranges()[1].start() as u64;
|
||||||
auxtbl.set(AuxKey::AT_ENTRY, program_entry as u64)?;
|
auxtbl.set(AuxKey::AT_BASE, ldso_elf_base)?;
|
||||||
|
|
||||||
let ldso_base = process_vm.get_ldso_code_range().start();
|
|
||||||
auxtbl.set(AuxKey::AT_BASE, ldso_base as u64)?;
|
|
||||||
|
|
||||||
auxtbl.set(AuxKey::AT_SYSINFO, 123)?;
|
|
||||||
|
|
||||||
let syscall_addr = __occlum_syscall as *const () as u64;
|
let syscall_addr = __occlum_syscall as *const () as u64;
|
||||||
auxtbl.set(AuxKey::AT_OCCLUM_ENTRY, syscall_addr)?;
|
auxtbl.set(AuxKey::AT_OCCLUM_ENTRY, syscall_addr)?;
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use std::slice;
|
|
||||||
use xmas_elf::program::ProgramHeader;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct Segment {
|
|
||||||
// Static info from ELF
|
|
||||||
mem_addr: usize,
|
|
||||||
mem_align: usize,
|
|
||||||
mem_size: usize,
|
|
||||||
file_offset: usize,
|
|
||||||
file_size: usize,
|
|
||||||
// Runtime info after loaded
|
|
||||||
runtime_base_addr: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const PERM_R: u32 = 0x1;
|
|
||||||
pub const PERM_W: u32 = 0x2;
|
|
||||||
pub const PERM_X: u32 = 0x4;
|
|
||||||
|
|
||||||
impl Segment {
|
|
||||||
pub fn get_mem_addr(&self) -> usize {
|
|
||||||
self.mem_addr
|
|
||||||
}
|
|
||||||
pub fn get_mem_align(&self) -> usize {
|
|
||||||
self.mem_align
|
|
||||||
}
|
|
||||||
pub fn get_mem_size(&self) -> usize {
|
|
||||||
self.mem_size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_program_header(ph: &ProgramHeader) -> Result<Segment> {
|
|
||||||
let ph64 = match ph {
|
|
||||||
ProgramHeader::Ph32(ph) => {
|
|
||||||
return_errno!(ENOEXEC, "not support 32-bit ELF");
|
|
||||||
}
|
|
||||||
ProgramHeader::Ph64(ph64) => ph64,
|
|
||||||
};
|
|
||||||
if ph64.align > 1 && ((ph64.offset % ph64.align) != (ph64.virtual_addr % ph64.align)) {
|
|
||||||
return_errno!(
|
|
||||||
EINVAL,
|
|
||||||
"memory address and file offset is not equal, per modulo"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ph64.mem_size < ph64.file_size {
|
|
||||||
return_errno!(EINVAL, "memory size must be greater than file size");
|
|
||||||
}
|
|
||||||
if !ph64.align.is_power_of_two() {
|
|
||||||
return_errno!(EINVAL, "memory alignment must be a power of two");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Segment {
|
|
||||||
mem_addr: ph64.virtual_addr as usize,
|
|
||||||
mem_align: ph64.align as usize,
|
|
||||||
mem_size: ph64.mem_size as usize,
|
|
||||||
file_offset: ph64.offset as usize,
|
|
||||||
file_size: ph64.file_size as usize,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_from_file(&self, elf_buf: &[u8]) {
|
|
||||||
let mut target_buf = unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
(self.runtime_base_addr.unwrap() + self.mem_addr) as *mut u8,
|
|
||||||
self.mem_size,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
target_buf[0..self.file_size]
|
|
||||||
.copy_from_slice(&elf_buf[self.file_offset..(self.file_offset + self.file_size)]);
|
|
||||||
#[cfg(feature = "integrity_only_opt")]
|
|
||||||
for i in &mut target_buf[self.file_size..self.mem_size] {
|
|
||||||
*i = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_runtime_base(&mut self, runtime_base_addr: usize) {
|
|
||||||
self.runtime_base_addr = Some(runtime_base_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mprotect(&mut self, perm: u32) {
|
|
||||||
panic!("Not implemented yet!");
|
|
||||||
/*
|
|
||||||
unsafe {
|
|
||||||
trts_mprotect(self.start_addr, self.end_addr - self.start_addr,
|
|
||||||
perm as u64);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_code_segment(elf_file: &ElfFile) -> Result<Segment> {
|
|
||||||
let code_ph = elf_helper::get_code_program_header(elf_file)
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to get the program header of code"))?;
|
|
||||||
Segment::from_program_header(&code_ph)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data_segment(elf_file: &ElfFile) -> Result<Segment> {
|
|
||||||
let data_ph = elf_helper::get_data_program_header(elf_file)
|
|
||||||
.map_err(|e| errno!(ENOEXEC, "failed to get the program header of code"))?;
|
|
||||||
Segment::from_program_header(&data_ph)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[link(name = "sgx_trts")]
|
|
||||||
extern "C" {
|
|
||||||
// XXX: trts_mprotect is a private SGX function that is not supposed to be
|
|
||||||
// used by external users. At least, this is the case for SGX v2.2. To use
|
|
||||||
// this function, we need to modify Intel SGX SDK slightly. I suppose
|
|
||||||
// this functionality will be exposed to external users as an SGX API in
|
|
||||||
// the future.
|
|
||||||
pub fn trts_mprotect(start: size_t, size: size_t, perms: uint64_t) -> sgx_status_t;
|
|
||||||
}
|
|
@ -125,8 +125,6 @@ fn guess_user_stack_bound(vm: &ProcessVM, user_rsp: usize) -> Result<&VMRange> {
|
|||||||
Ok(vm.get_stack_range())
|
Ok(vm.get_stack_range())
|
||||||
} else if vm.get_heap_range().contains(user_rsp) {
|
} else if vm.get_heap_range().contains(user_rsp) {
|
||||||
Ok(vm.get_heap_range())
|
Ok(vm.get_heap_range())
|
||||||
} else if vm.get_data_range().contains(user_rsp) {
|
|
||||||
Ok(vm.get_data_range())
|
|
||||||
}
|
}
|
||||||
// Invalid
|
// Invalid
|
||||||
else {
|
else {
|
||||||
|
@ -5,10 +5,15 @@ use std::fmt;
|
|||||||
|
|
||||||
mod process_vm;
|
mod process_vm;
|
||||||
mod user_space_vm;
|
mod user_space_vm;
|
||||||
|
mod vm_layout;
|
||||||
mod vm_manager;
|
mod vm_manager;
|
||||||
|
mod vm_range;
|
||||||
|
|
||||||
|
use self::vm_layout::VMLayout;
|
||||||
|
use self::vm_manager::{VMManager, VMMapOptionsBuilder};
|
||||||
|
|
||||||
pub use self::process_vm::{MMapFlags, ProcessVM, ProcessVMBuilder, VMPerms};
|
pub use self::process_vm::{MMapFlags, ProcessVM, ProcessVMBuilder, VMPerms};
|
||||||
pub use self::vm_manager::VMRange;
|
pub use self::vm_range::VMRange;
|
||||||
|
|
||||||
pub fn do_mmap(
|
pub fn do_mmap(
|
||||||
addr: usize,
|
addr: usize,
|
||||||
|
@ -1,53 +1,46 @@
|
|||||||
use super::super::config;
|
|
||||||
use super::user_space_vm::{UserSpaceVMManager, UserSpaceVMRange, USER_SPACE_VM_MANAGER};
|
|
||||||
use super::vm_manager::{
|
|
||||||
VMInitializer, VMManager, VMMapAddr, VMMapOptions, VMMapOptionsBuilder, VMRange,
|
|
||||||
};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::slice;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
use super::config;
|
||||||
pub struct ProcessVMBuilder {
|
use super::process::{ElfFile, ProgramHeaderExt};
|
||||||
code_size: usize,
|
use super::user_space_vm::{UserSpaceVMManager, UserSpaceVMRange, USER_SPACE_VM_MANAGER};
|
||||||
data_size: usize,
|
use super::vm_manager::{VMInitializer, VMManager, VMMapAddr, VMMapOptions, VMMapOptionsBuilder};
|
||||||
ldso_code_size: Option<usize>,
|
|
||||||
ldso_data_size: Option<usize>,
|
#[derive(Debug)]
|
||||||
|
pub struct ProcessVMBuilder<'a, 'b> {
|
||||||
|
elfs: Vec<&'b ElfFile<'a>>,
|
||||||
heap_size: Option<usize>,
|
heap_size: Option<usize>,
|
||||||
stack_size: Option<usize>,
|
stack_size: Option<usize>,
|
||||||
mmap_size: Option<usize>,
|
mmap_size: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_setter_for_process_vm_builder {
|
impl<'a, 'b> ProcessVMBuilder<'a, 'b> {
|
||||||
($field: ident) => {
|
pub fn new(elfs: Vec<&'b ElfFile<'a>>) -> ProcessVMBuilder<'a, 'b> {
|
||||||
pub fn $field(mut self, size: usize) -> Self {
|
ProcessVMBuilder {
|
||||||
self.$field = Some(size);
|
elfs: elfs,
|
||||||
|
heap_size: None,
|
||||||
|
stack_size: None,
|
||||||
|
mmap_size: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_heap_size(&mut self, heap_size: usize) -> &mut Self {
|
||||||
|
self.heap_size = Some(heap_size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
pub fn set_stack_size(&mut self, stack_size: usize) -> &mut Self {
|
||||||
|
self.stack_size = Some(stack_size);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcessVMBuilder {
|
pub fn set_mmap_size(&mut self, mmap_size: usize) -> &mut Self {
|
||||||
pub fn new(code_size: usize, data_size: usize) -> ProcessVMBuilder {
|
self.mmap_size = Some(mmap_size);
|
||||||
ProcessVMBuilder {
|
self
|
||||||
code_size,
|
|
||||||
data_size,
|
|
||||||
..ProcessVMBuilder::default()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl_setter_for_process_vm_builder!(ldso_data_size);
|
|
||||||
impl_setter_for_process_vm_builder!(ldso_code_size);
|
|
||||||
impl_setter_for_process_vm_builder!(heap_size);
|
|
||||||
impl_setter_for_process_vm_builder!(stack_size);
|
|
||||||
impl_setter_for_process_vm_builder!(mmap_size);
|
|
||||||
|
|
||||||
pub fn build(self) -> Result<ProcessVM> {
|
pub fn build(self) -> Result<ProcessVM> {
|
||||||
self.validate()?;
|
self.validate()?;
|
||||||
|
|
||||||
let code_size = self.code_size;
|
|
||||||
let data_size = self.data_size;
|
|
||||||
let ldso_code_size = self.ldso_code_size.unwrap_or(0);
|
|
||||||
let ldso_data_size = self.ldso_data_size.unwrap_or(0);
|
|
||||||
let heap_size = self
|
let heap_size = self
|
||||||
.heap_size
|
.heap_size
|
||||||
.unwrap_or(config::LIBOS_CONFIG.process.default_heap_size);
|
.unwrap_or(config::LIBOS_CONFIG.process.default_heap_size);
|
||||||
@ -57,52 +50,103 @@ impl ProcessVMBuilder {
|
|||||||
let mmap_size = self
|
let mmap_size = self
|
||||||
.mmap_size
|
.mmap_size
|
||||||
.unwrap_or(config::LIBOS_CONFIG.process.default_mmap_size);
|
.unwrap_or(config::LIBOS_CONFIG.process.default_mmap_size);
|
||||||
let range_sizes = vec![
|
|
||||||
code_size,
|
// Before allocating memory, let's first calcualte how much memory
|
||||||
data_size,
|
// we need in total by iterating the memory layouts required by
|
||||||
ldso_code_size,
|
// all the memory regions
|
||||||
ldso_data_size,
|
let elf_layouts: Vec<VMLayout> = self
|
||||||
heap_size,
|
.elfs
|
||||||
stack_size,
|
.iter()
|
||||||
mmap_size,
|
.map(|elf| {
|
||||||
|
elf.program_headers()
|
||||||
|
.filter(|segment| segment.loadable())
|
||||||
|
.fold(VMLayout::new_empty(), |mut elf_layout, segment| {
|
||||||
|
let segment_size = (segment.virtual_addr() + segment.mem_size()) as usize;
|
||||||
|
let segment_align = segment.align() as usize;
|
||||||
|
let segment_layout = VMLayout::new(segment_size, segment_align).unwrap();
|
||||||
|
elf_layout.extend(&segment_layout);
|
||||||
|
elf_layout
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let other_layouts = vec![
|
||||||
|
VMLayout::new(heap_size, PAGE_SIZE)?,
|
||||||
|
VMLayout::new(stack_size, PAGE_SIZE)?,
|
||||||
|
VMLayout::new(mmap_size, PAGE_SIZE)?,
|
||||||
];
|
];
|
||||||
|
let process_layout = elf_layouts.iter().chain(other_layouts.iter()).fold(
|
||||||
|
VMLayout::new_empty(),
|
||||||
|
|mut process_layout, sub_layout| {
|
||||||
|
process_layout.extend(&sub_layout);
|
||||||
|
process_layout
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now that we end up with the memory layout required by the process,
|
||||||
|
// let's allocate the memory for the process
|
||||||
let process_range = {
|
let process_range = {
|
||||||
let total_size = range_sizes.iter().sum();
|
// TODO: ensure alignment through USER_SPACE_VM_MANAGER, not by
|
||||||
USER_SPACE_VM_MANAGER.alloc(total_size)?
|
// preserving extra space for alignment
|
||||||
|
USER_SPACE_VM_MANAGER.alloc(process_layout.align() + process_layout.size())?
|
||||||
};
|
};
|
||||||
|
let process_base = process_range.range().start();
|
||||||
|
|
||||||
let vm_ranges = {
|
// Init the memory for ELFs in the process
|
||||||
let mut curr_addr = process_range.range().start();
|
let elf_ranges: Vec<VMRange> = {
|
||||||
let mut vm_ranges = Vec::new();
|
let mut min_elf_start = process_base;
|
||||||
for range_size in &range_sizes {
|
elf_layouts
|
||||||
let range_start = curr_addr;
|
.iter()
|
||||||
let range_end = curr_addr + range_size;
|
.map(|elf_layout| {
|
||||||
let range = VMRange::from(range_start, range_end)?;
|
let new_elf_range = VMRange::new_with_layout(elf_layout, min_elf_start);
|
||||||
vm_ranges.push(range);
|
min_elf_start = new_elf_range.end();
|
||||||
|
new_elf_range
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
self.elfs
|
||||||
|
.iter()
|
||||||
|
.zip(elf_ranges.iter())
|
||||||
|
.try_for_each(|(elf, elf_range)| Self::init_elf_memory(elf_range, elf))?;
|
||||||
|
|
||||||
curr_addr = range_end;
|
// Init the heap memory in the process
|
||||||
|
let heap_layout = &other_layouts[0];
|
||||||
|
let heap_min_start = {
|
||||||
|
let last_elf_range = elf_ranges.iter().last().unwrap();
|
||||||
|
last_elf_range.end()
|
||||||
|
};
|
||||||
|
let heap_range = VMRange::new_with_layout(heap_layout, heap_min_start);
|
||||||
|
unsafe {
|
||||||
|
let heap_buf = heap_range.as_slice_mut();
|
||||||
|
for b in heap_buf {
|
||||||
|
*b = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
vm_ranges
|
|
||||||
};
|
|
||||||
let code_range = *&vm_ranges[0];
|
|
||||||
let data_range = *&vm_ranges[1];
|
|
||||||
let ldso_code_range = *&vm_ranges[2];
|
|
||||||
let ldso_data_range = *&vm_ranges[3];
|
|
||||||
let heap_range = *&vm_ranges[4];
|
|
||||||
let stack_range = *&vm_ranges[5];
|
|
||||||
let mmap_range = *&vm_ranges[6];
|
|
||||||
|
|
||||||
let brk = heap_range.start();
|
let brk = heap_range.start();
|
||||||
|
|
||||||
|
// Init the stack memory in the process
|
||||||
|
let stack_layout = &other_layouts[1];
|
||||||
|
let stack_min_start = heap_range.end();
|
||||||
|
let stack_range = VMRange::new_with_layout(stack_layout, stack_min_start);
|
||||||
|
// Note: we do not need to fill zeros for stack
|
||||||
|
|
||||||
|
// Init the mmap memory in the process
|
||||||
|
let mmap_layout = &other_layouts[2];
|
||||||
|
let mmap_min_start = stack_range.end();
|
||||||
|
let mmap_range = VMRange::new_with_layout(mmap_layout, mmap_min_start);
|
||||||
let mmap_manager = VMManager::from(mmap_range.start(), mmap_range.size())?;
|
let mmap_manager = VMManager::from(mmap_range.start(), mmap_range.size())?;
|
||||||
|
// Note: we do not need to fill zeros of the mmap region.
|
||||||
|
// VMManager will fill zeros (if necessary) on mmap.
|
||||||
|
|
||||||
|
debug_assert!(elf_ranges
|
||||||
|
.iter()
|
||||||
|
.all(|elf_range| process_range.range().is_superset_of(elf_range)));
|
||||||
|
debug_assert!(process_range.range().is_superset_of(&heap_range));
|
||||||
|
debug_assert!(process_range.range().is_superset_of(&stack_range));
|
||||||
|
debug_assert!(process_range.range().is_superset_of(&mmap_range));
|
||||||
|
|
||||||
Ok(ProcessVM {
|
Ok(ProcessVM {
|
||||||
process_range,
|
process_range,
|
||||||
code_range,
|
elf_ranges,
|
||||||
data_range,
|
|
||||||
ldso_code_range,
|
|
||||||
ldso_data_range,
|
|
||||||
heap_range,
|
heap_range,
|
||||||
stack_range,
|
stack_range,
|
||||||
brk,
|
brk,
|
||||||
@ -110,8 +154,45 @@ impl ProcessVMBuilder {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement this!
|
|
||||||
fn validate(&self) -> Result<()> {
|
fn validate(&self) -> Result<()> {
|
||||||
|
let validate_size = |size_opt| -> Result<()> {
|
||||||
|
if let Some(size) = size_opt {
|
||||||
|
if size == 0 || size % PAGE_SIZE != 0 {
|
||||||
|
return_errno!(EINVAL, "invalid size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
validate_size(self.heap_size)?;
|
||||||
|
validate_size(self.stack_size)?;
|
||||||
|
validate_size(self.mmap_size)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_elf_memory(elf_range: &VMRange, elf_file: &ElfFile) -> Result<()> {
|
||||||
|
// Destination buffer: ELF appeared in the process
|
||||||
|
let elf_proc_buf = unsafe { elf_range.as_slice_mut() };
|
||||||
|
// Source buffer: ELF stored in the ELF file
|
||||||
|
let elf_file_buf = elf_file.as_slice();
|
||||||
|
// Init all loadable segements
|
||||||
|
let loadable_segments = elf_file
|
||||||
|
.program_headers()
|
||||||
|
.filter(|segment| segment.loadable())
|
||||||
|
.for_each(|segment| {
|
||||||
|
let file_size = segment.file_size() as usize;
|
||||||
|
let file_offset = segment.offset() as usize;
|
||||||
|
let mem_addr = segment.virtual_addr() as usize;
|
||||||
|
let mem_size = segment.mem_size() as usize;
|
||||||
|
debug_assert!(file_size <= mem_size);
|
||||||
|
|
||||||
|
// The first file_size bytes are loaded from the ELF file
|
||||||
|
elf_proc_buf[mem_addr..mem_addr + file_size]
|
||||||
|
.copy_from_slice(&elf_file_buf[file_offset..file_offset + file_size]);
|
||||||
|
// The remaining (mem_size - file_size) bytes are zeros
|
||||||
|
for b in &mut elf_proc_buf[mem_addr + file_size..mem_addr + mem_size] {
|
||||||
|
*b = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,10 +201,7 @@ impl ProcessVMBuilder {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProcessVM {
|
pub struct ProcessVM {
|
||||||
process_range: UserSpaceVMRange,
|
process_range: UserSpaceVMRange,
|
||||||
code_range: VMRange,
|
elf_ranges: Vec<VMRange>,
|
||||||
data_range: VMRange,
|
|
||||||
ldso_code_range: VMRange,
|
|
||||||
ldso_data_range: VMRange,
|
|
||||||
heap_range: VMRange,
|
heap_range: VMRange,
|
||||||
stack_range: VMRange,
|
stack_range: VMRange,
|
||||||
brk: usize,
|
brk: usize,
|
||||||
@ -134,11 +212,8 @@ impl Default for ProcessVM {
|
|||||||
fn default() -> ProcessVM {
|
fn default() -> ProcessVM {
|
||||||
ProcessVM {
|
ProcessVM {
|
||||||
process_range: USER_SPACE_VM_MANAGER.alloc_dummy(),
|
process_range: USER_SPACE_VM_MANAGER.alloc_dummy(),
|
||||||
code_range: Default::default(),
|
elf_ranges: Default::default(),
|
||||||
data_range: Default::default(),
|
|
||||||
heap_range: Default::default(),
|
heap_range: Default::default(),
|
||||||
ldso_code_range: Default::default(),
|
|
||||||
ldso_data_range: Default::default(),
|
|
||||||
stack_range: Default::default(),
|
stack_range: Default::default(),
|
||||||
brk: Default::default(),
|
brk: Default::default(),
|
||||||
mmap_manager: Default::default(),
|
mmap_manager: Default::default(),
|
||||||
@ -151,20 +226,8 @@ impl ProcessVM {
|
|||||||
self.process_range.range()
|
self.process_range.range()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_code_range(&self) -> &VMRange {
|
pub fn get_elf_ranges(&self) -> &[VMRange] {
|
||||||
&self.code_range
|
&self.elf_ranges
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data_range(&self) -> &VMRange {
|
|
||||||
&self.data_range
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_ldso_code_range(&self) -> &VMRange {
|
|
||||||
&self.ldso_code_range
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_ldso_data_range(&self) -> &VMRange {
|
|
||||||
&self.ldso_data_range
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_heap_range(&self) -> &VMRange {
|
pub fn get_heap_range(&self) -> &VMRange {
|
||||||
@ -323,7 +386,7 @@ impl VMPerms {
|
|||||||
|
|
||||||
unsafe fn fill_zeros(addr: usize, size: usize) {
|
unsafe fn fill_zeros(addr: usize, size: usize) {
|
||||||
let ptr = addr as *mut u8;
|
let ptr = addr as *mut u8;
|
||||||
let buf = slice::from_raw_parts_mut(ptr, size);
|
let buf = std::slice::from_raw_parts_mut(ptr, size);
|
||||||
for b in buf {
|
for b in buf {
|
||||||
*b = 0;
|
*b = 0;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use super::vm_manager::{VMManager, VMMapOptions, VMMapOptionsBuilder, VMRange};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// The virtual memory manager for the entire user space
|
/// The virtual memory manager for the entire user space
|
||||||
|
57
src/libos/src/vm/vm_layout.rs
Normal file
57
src/libos/src/vm/vm_layout.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub struct VMLayout {
|
||||||
|
size: usize,
|
||||||
|
align: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VMLayout {
|
||||||
|
pub fn new(size: usize, align: usize) -> Result<VMLayout> {
|
||||||
|
if !align.is_power_of_two() || align % PAGE_SIZE != 0 {
|
||||||
|
return_errno!(EINVAL, "invalid layout");
|
||||||
|
}
|
||||||
|
Ok(VMLayout { size, align })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_empty() -> VMLayout {
|
||||||
|
VMLayout {
|
||||||
|
size: 0,
|
||||||
|
align: PAGE_SIZE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend(&mut self, more_space: &VMLayout) -> &mut Self {
|
||||||
|
if more_space.size == 0 {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.size = align_up(self.size, more_space.align) + more_space.size;
|
||||||
|
self.align = max(self.align, more_space.align);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn align(&self) -> usize {
|
||||||
|
self.align
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for VMLayout {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"VMLayout {{ size: 0x{:x?}, align: 0x{:x?} }}",
|
||||||
|
self.size, self.align
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VMLayout {
|
||||||
|
fn default() -> VMLayout {
|
||||||
|
VMLayout::new_empty()
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ impl Default for VMInitializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VMInitializer {
|
impl VMInitializer {
|
||||||
pub fn initialize(&self, buf: &mut [u8]) -> Result<()> {
|
pub fn init_slice(&self, buf: &mut [u8]) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
VMInitializer::DoNothing() => {
|
VMInitializer::DoNothing() => {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
@ -131,12 +131,12 @@ pub struct VMManager {
|
|||||||
|
|
||||||
impl VMManager {
|
impl VMManager {
|
||||||
pub fn from(addr: usize, size: usize) -> Result<VMManager> {
|
pub fn from(addr: usize, size: usize) -> Result<VMManager> {
|
||||||
let range = VMRange::from(addr, addr + size)?;
|
let range = VMRange::new(addr, addr + size)?;
|
||||||
let sub_ranges = {
|
let sub_ranges = {
|
||||||
let start = range.start();
|
let start = range.start();
|
||||||
let end = range.end();
|
let end = range.end();
|
||||||
let start_sentry = VMRange::from(start, start)?;
|
let start_sentry = VMRange::new(start, start)?;
|
||||||
let end_sentry = VMRange::from(end, end)?;
|
let end_sentry = VMRange::new(end, end)?;
|
||||||
vec![start_sentry, end_sentry]
|
vec![start_sentry, end_sentry]
|
||||||
};
|
};
|
||||||
Ok(VMManager { range, sub_ranges })
|
Ok(VMManager { range, sub_ranges })
|
||||||
@ -162,10 +162,8 @@ impl VMManager {
|
|||||||
|
|
||||||
// Initialize the memory of the new subrange
|
// Initialize the memory of the new subrange
|
||||||
unsafe {
|
unsafe {
|
||||||
let buf_ptr = new_subrange.start() as *mut u8;
|
let buf = new_subrange.as_slice_mut();
|
||||||
let buf_size = new_subrange.size() as usize;
|
options.initializer.init_slice(buf)?;
|
||||||
let buf = std::slice::from_raw_parts_mut(buf_ptr, buf_size);
|
|
||||||
options.initializer.initialize(buf)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// After initializing, we can safely add the new subrange
|
// After initializing, we can safely add the new subrange
|
||||||
@ -182,7 +180,7 @@ impl VMManager {
|
|||||||
align_up(size, PAGE_SIZE)
|
align_up(size, PAGE_SIZE)
|
||||||
};
|
};
|
||||||
let munmap_range = {
|
let munmap_range = {
|
||||||
let munmap_range = VMRange::from(addr, addr + size)?;
|
let munmap_range = VMRange::new(addr, addr + size)?;
|
||||||
|
|
||||||
let effective_munmap_range_opt = munmap_range.intersect(&self.range);
|
let effective_munmap_range_opt = munmap_range.intersect(&self.range);
|
||||||
if effective_munmap_range_opt.is_none() {
|
if effective_munmap_range_opt.is_none() {
|
||||||
@ -310,99 +308,3 @@ impl VMManager {
|
|||||||
new_subrange
|
new_subrange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Debug, PartialEq)]
|
|
||||||
pub struct VMRange {
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VMRange {
|
|
||||||
pub fn from(start: usize, end: usize) -> Result<VMRange> {
|
|
||||||
if start % PAGE_SIZE != 0 || end % PAGE_SIZE != 0 || start > end {
|
|
||||||
return_errno!(EINVAL, "invalid start or end");
|
|
||||||
}
|
|
||||||
Ok(VMRange {
|
|
||||||
start: start,
|
|
||||||
end: end,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_unchecked(start: usize, end: usize) -> VMRange {
|
|
||||||
debug_assert!(start % PAGE_SIZE == 0);
|
|
||||||
debug_assert!(end % PAGE_SIZE == 0);
|
|
||||||
debug_assert!(start <= end);
|
|
||||||
VMRange {
|
|
||||||
start: start,
|
|
||||||
end: end,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(&self) -> usize {
|
|
||||||
self.start
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end(&self) -> usize {
|
|
||||||
self.end
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(&self) -> usize {
|
|
||||||
self.end - self.start
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(&mut self, new_size: usize) {
|
|
||||||
self.end = self.start + new_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty(&self) -> bool {
|
|
||||||
self.start == self.end
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_superset_of(&self, other: &VMRange) -> bool {
|
|
||||||
self.start() <= other.start() && other.end() <= self.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(&self, addr: usize) -> bool {
|
|
||||||
self.start() <= addr && addr < self.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subtract(&self, other: &VMRange) -> Vec<VMRange> {
|
|
||||||
let self_start = self.start();
|
|
||||||
let self_end = self.end();
|
|
||||||
let other_start = other.start();
|
|
||||||
let other_end = other.end();
|
|
||||||
|
|
||||||
match (self_start < other_start, other_end < self_end) {
|
|
||||||
(false, false) => Vec::new(),
|
|
||||||
(false, true) => unsafe {
|
|
||||||
vec![VMRange::from_unchecked(self_start.max(other_end), self_end)]
|
|
||||||
},
|
|
||||||
(true, false) => unsafe {
|
|
||||||
vec![VMRange::from_unchecked(
|
|
||||||
self_start,
|
|
||||||
self_end.min(other_start),
|
|
||||||
)]
|
|
||||||
},
|
|
||||||
(true, true) => unsafe {
|
|
||||||
vec![
|
|
||||||
VMRange::from_unchecked(self_start, other_start),
|
|
||||||
VMRange::from_unchecked(other_end, self_end),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn intersect(&self, other: &VMRange) -> Option<VMRange> {
|
|
||||||
let intersection_start = self.start().max(other.start());
|
|
||||||
let intersection_end = self.end().min(other.end());
|
|
||||||
if intersection_start > intersection_end {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
Some(VMRange::from_unchecked(
|
|
||||||
intersection_start,
|
|
||||||
intersection_end,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
137
src/libos/src/vm/vm_range.rs
Normal file
137
src/libos/src/vm/vm_range.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, PartialEq)]
|
||||||
|
pub struct VMRange {
|
||||||
|
pub(super) start: usize,
|
||||||
|
pub(super) end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VMRange {
|
||||||
|
pub fn new(start: usize, end: usize) -> Result<VMRange> {
|
||||||
|
if start % PAGE_SIZE != 0 || end % PAGE_SIZE != 0 || start > end {
|
||||||
|
return_errno!(EINVAL, "invalid start or end");
|
||||||
|
}
|
||||||
|
Ok(VMRange {
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_empty(start: usize) -> Result<VMRange> {
|
||||||
|
if start % PAGE_SIZE != 0 {
|
||||||
|
return_errno!(EINVAL, "invalid start or end");
|
||||||
|
}
|
||||||
|
Ok(VMRange {
|
||||||
|
start: start,
|
||||||
|
end: start,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_layout(layout: &VMLayout, min_start: usize) -> VMRange {
|
||||||
|
let start = align_up(min_start, layout.align());
|
||||||
|
let end = align_up(start + layout.size(), PAGE_SIZE);
|
||||||
|
unsafe { VMRange::from_unchecked(start, end) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn from_unchecked(start: usize, end: usize) -> VMRange {
|
||||||
|
debug_assert!(start % PAGE_SIZE == 0);
|
||||||
|
debug_assert!(end % PAGE_SIZE == 0);
|
||||||
|
debug_assert!(start <= end);
|
||||||
|
VMRange {
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&self) -> usize {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(&self) -> usize {
|
||||||
|
self.end
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.end - self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, new_size: usize) {
|
||||||
|
self.end = self.start + new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty(&self) -> bool {
|
||||||
|
self.start == self.end
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_superset_of(&self, other: &VMRange) -> bool {
|
||||||
|
self.start() <= other.start() && other.end() <= self.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, addr: usize) -> bool {
|
||||||
|
self.start() <= addr && addr < self.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subtract(&self, other: &VMRange) -> Vec<VMRange> {
|
||||||
|
let self_start = self.start();
|
||||||
|
let self_end = self.end();
|
||||||
|
let other_start = other.start();
|
||||||
|
let other_end = other.end();
|
||||||
|
|
||||||
|
match (self_start < other_start, other_end < self_end) {
|
||||||
|
(false, false) => Vec::new(),
|
||||||
|
(false, true) => unsafe {
|
||||||
|
vec![VMRange::from_unchecked(self_start.max(other_end), self_end)]
|
||||||
|
},
|
||||||
|
(true, false) => unsafe {
|
||||||
|
vec![VMRange::from_unchecked(
|
||||||
|
self_start,
|
||||||
|
self_end.min(other_start),
|
||||||
|
)]
|
||||||
|
},
|
||||||
|
(true, true) => unsafe {
|
||||||
|
vec![
|
||||||
|
VMRange::from_unchecked(self_start, other_start),
|
||||||
|
VMRange::from_unchecked(other_end, self_end),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect(&self, other: &VMRange) -> Option<VMRange> {
|
||||||
|
let intersection_start = self.start().max(other.start());
|
||||||
|
let intersection_end = self.end().min(other.end());
|
||||||
|
if intersection_start > intersection_end {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
Some(VMRange::from_unchecked(
|
||||||
|
intersection_start,
|
||||||
|
intersection_end,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_slice(&self) -> &[u8] {
|
||||||
|
let buf_ptr = self.start() as *const u8;
|
||||||
|
let buf_size = self.size() as usize;
|
||||||
|
std::slice::from_raw_parts(buf_ptr, buf_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_slice_mut(&self) -> &mut [u8] {
|
||||||
|
let buf_ptr = self.start() as *mut u8;
|
||||||
|
let buf_size = self.size() as usize;
|
||||||
|
std::slice::from_raw_parts_mut(buf_ptr, buf_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for VMRange {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"VMRange {{ start: 0x{:x?}, end: 0x{:x?}, size: 0x{:x?} }}",
|
||||||
|
self.start,
|
||||||
|
self.end,
|
||||||
|
self.size()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
},
|
},
|
||||||
"process": {
|
"process": {
|
||||||
"default_stack_size": "4MB",
|
"default_stack_size": "4MB",
|
||||||
"default_heap_size": "16MB",
|
"default_heap_size": "8MB",
|
||||||
"default_mmap_size": "32MB"
|
"default_mmap_size": "32MB"
|
||||||
},
|
},
|
||||||
"env": [
|
"env": [
|
||||||
|
Loading…
Reference in New Issue
Block a user