Refactor the elf loading logics

This commit is contained in:
Chen Chengjun
2024-08-12 16:14:20 +08:00
committed by Tate, Hongliang Tian
parent 950fbc131b
commit 2ca7b28bbc

View File

@ -7,7 +7,7 @@
//! When create a process from elf file, we will use the elf_load_info to construct the VmSpace //! When create a process from elf file, we will use the elf_load_info to construct the VmSpace
use align_ext::AlignExt; use align_ext::AlignExt;
use aster_rights::{Full, Rights}; use aster_rights::Full;
use ostd::mm::VmIo; use ostd::mm::VmIo;
use xmas_elf::program::{self, ProgramHeader64}; use xmas_elf::program::{self, ProgramHeader64};
@ -23,12 +23,8 @@ use crate::{
process_vm::{AuxKey, AuxVec, ProcessVm}, process_vm::{AuxKey, AuxVec, ProcessVm},
TermStatus, TermStatus,
}, },
vdso::vdso_vmo, vdso::{vdso_vmo, VDSO_VMO_SIZE},
vm::{ vm::{perms::VmPerms, util::duplicate_frame, vmar::Vmar, vmo::VmoRightsOp},
perms::VmPerms,
vmar::Vmar,
vmo::{Vmo, VmoOptions, VmoRightsOp},
},
}; };
/// Loads elf to the process vm. /// Loads elf to the process vm.
@ -197,7 +193,7 @@ impl ElfLoadInfo {
} }
} }
/// init vmo for each segment and then map segment to root vmar /// Inits VMO for each segment and then map segment to root vmar
pub fn map_segment_vmos(elf: &Elf, root_vmar: &Vmar<Full>, elf_file: &Dentry) -> Result<Vaddr> { pub fn map_segment_vmos(elf: &Elf, root_vmar: &Vmar<Full>, elf_file: &Dentry) -> Result<Vaddr> {
// all segments of the shared object must be mapped to a continuous vm range // all segments of the shared object must be mapped to a continuous vm range
// to ensure the relative offset of each segment not changed. // to ensure the relative offset of each segment not changed.
@ -212,14 +208,7 @@ pub fn map_segment_vmos(elf: &Elf, root_vmar: &Vmar<Full>, elf_file: &Dentry) ->
.map_err(|_| Error::with_message(Errno::ENOEXEC, "parse program header type fails"))?; .map_err(|_| Error::with_message(Errno::ENOEXEC, "parse program header type fails"))?;
if type_ == program::Type::Load { if type_ == program::Type::Load {
check_segment_align(program_header)?; check_segment_align(program_header)?;
let (vmo, anonymous_map_size) = init_segment_vmo(program_header, elf_file)?; map_segment_vmo(program_header, elf_file, root_vmar, base_addr)?;
map_segment_vmo(
program_header,
vmo,
anonymous_map_size,
root_vmar,
base_addr,
)?;
} }
} }
Ok(base_addr) Ok(base_addr)
@ -245,49 +234,18 @@ fn base_map_addr(elf: &Elf, root_vmar: &Vmar<Full>) -> Result<Vaddr> {
"executable file does not has loadable sections", "executable file does not has loadable sections",
))?; ))?;
let map_size = elf_size.align_up(PAGE_SIZE); let map_size = elf_size.align_up(PAGE_SIZE);
let vmo = VmoOptions::<Rights>::new(0).alloc()?; let vmar_map_options = root_vmar.new_map(map_size, VmPerms::empty())?;
let vmar_map_options = root_vmar.new_map(vmo, VmPerms::empty())?.size(map_size);
vmar_map_options.build() vmar_map_options.build()
} }
/// map the segment vmo to root_vmar /// Creates and map the corresponding segment VMO to `root_vmar`.
/// If needed, create additional anonymous mapping to represents .bss segment.
fn map_segment_vmo( fn map_segment_vmo(
program_header: &ProgramHeader64, program_header: &ProgramHeader64,
vmo: Vmo, elf_file: &Dentry,
anonymous_map_size: usize,
root_vmar: &Vmar<Full>, root_vmar: &Vmar<Full>,
base_addr: Vaddr, base_addr: Vaddr,
) -> Result<()> { ) -> Result<()> {
let perms = parse_segment_perm(program_header.flags);
let offset = (program_header.virtual_addr as Vaddr).align_down(PAGE_SIZE);
trace!(
"map segment vmo: virtual addr = 0x{:x}, size = 0x{:x}, perms = {:?}",
offset,
program_header.mem_size,
perms
);
let vmo_size = vmo.size();
let mut vm_map_options = root_vmar.new_map(vmo, perms)?.can_overwrite(true);
let offset = base_addr + offset;
vm_map_options = vm_map_options.offset(offset);
let map_addr = vm_map_options.build()?;
if anonymous_map_size > 0 {
let anonymous_vmo = {
let options: VmoOptions<Rights> = VmoOptions::new(anonymous_map_size);
options.alloc()?
};
let mut anonymous_map_options =
root_vmar.new_map(anonymous_vmo, perms)?.can_overwrite(true);
anonymous_map_options = anonymous_map_options.offset(offset + vmo_size);
let anonymous_map_addr = anonymous_map_options.build()?;
}
Ok(())
}
/// Create VMO for each segment. Return the segment VMO and the size of
/// additional anonymous mapping it needs.
fn init_segment_vmo(program_header: &ProgramHeader64, elf_file: &Dentry) -> Result<(Vmo, usize)> {
trace!( trace!(
"mem range = 0x{:x} - 0x{:x}, mem_size = 0x{:x}", "mem range = 0x{:x} - 0x{:x}, mem_size = 0x{:x}",
program_header.virtual_addr, program_header.virtual_addr,
@ -304,33 +262,29 @@ fn init_segment_vmo(program_header: &ProgramHeader64, elf_file: &Dentry) -> Resu
let file_offset = program_header.offset as usize; let file_offset = program_header.offset as usize;
let virtual_addr = program_header.virtual_addr as usize; let virtual_addr = program_header.virtual_addr as usize;
debug_assert!(file_offset % PAGE_SIZE == virtual_addr % PAGE_SIZE); debug_assert!(file_offset % PAGE_SIZE == virtual_addr % PAGE_SIZE);
let page_cache_vmo = { let segment_vmo = {
let inode = elf_file.inode(); let inode = elf_file.inode();
inode.page_cache().ok_or(Error::with_message( inode
Errno::ENOENT, .page_cache()
"executable has no page cache", .ok_or(Error::with_message(
))? Errno::ENOENT,
"executable has no page cache",
))?
.to_dyn()
.dup_independent()?
}; };
let vmo_size = {
let total_map_size = {
let vmap_start = virtual_addr.align_down(PAGE_SIZE); let vmap_start = virtual_addr.align_down(PAGE_SIZE);
let vmap_end = (virtual_addr + program_header.mem_size as usize).align_up(PAGE_SIZE); let vmap_end = (virtual_addr + program_header.mem_size as usize).align_up(PAGE_SIZE);
vmap_end - vmap_start vmap_end - vmap_start
}; };
let segment_vmo = { let (segment_offset, segment_size) = {
let parent_range = { let start = file_offset.align_down(PAGE_SIZE);
let start = file_offset.align_down(PAGE_SIZE); let end = (file_offset + program_header.file_size as usize).align_up(PAGE_SIZE);
let end = (file_offset + program_header.file_size as usize).align_up(PAGE_SIZE); debug_assert!(total_map_size >= (program_header.file_size as usize).align_up(PAGE_SIZE));
start..end (start, end - start)
};
debug_assert!(vmo_size >= (program_header.file_size as usize).align_up(PAGE_SIZE));
page_cache_vmo.new_cow_child(parent_range).alloc()?
};
let anonymous_map_size: usize = if vmo_size > segment_vmo.size() {
vmo_size - segment_vmo.size()
} else {
0
}; };
// Write zero as paddings. There are head padding and tail padding. // Write zero as paddings. There are head padding and tail padding.
@ -342,17 +296,61 @@ fn init_segment_vmo(program_header: &ProgramHeader64, elf_file: &Dentry) -> Resu
// Head padding. // Head padding.
let page_offset = file_offset % PAGE_SIZE; let page_offset = file_offset % PAGE_SIZE;
if page_offset != 0 { if page_offset != 0 {
let buffer = vec![0u8; page_offset]; let new_frame = {
segment_vmo.write_bytes(0, &buffer)?; let head_frame = segment_vmo.commit_page(segment_offset)?;
let new_frame = duplicate_frame(&head_frame)?;
let buffer = vec![0u8; page_offset];
new_frame.write_bytes(0, &buffer).unwrap();
new_frame
};
let head_idx = segment_offset / PAGE_SIZE;
segment_vmo.replace(new_frame, head_idx)?;
} }
// Tail padding. // Tail padding.
let segment_vmo_size = segment_vmo.size();
let tail_padding_offset = program_header.file_size as usize + page_offset; let tail_padding_offset = program_header.file_size as usize + page_offset;
if segment_vmo_size > tail_padding_offset { if segment_size > tail_padding_offset {
let buffer = vec![0u8; (segment_vmo_size - tail_padding_offset) % PAGE_SIZE]; let new_frame = {
segment_vmo.write_bytes(tail_padding_offset, &buffer)?; let tail_frame = segment_vmo.commit_page(segment_offset + tail_padding_offset)?;
let new_frame = duplicate_frame(&tail_frame)?;
let buffer = vec![0u8; (segment_size - tail_padding_offset) % PAGE_SIZE];
new_frame
.write_bytes(tail_padding_offset % PAGE_SIZE, &buffer)
.unwrap();
new_frame
};
let tail_idx = (segment_offset + tail_padding_offset) / PAGE_SIZE;
segment_vmo.replace(new_frame, tail_idx).unwrap();
} }
Ok((segment_vmo.to_dyn(), anonymous_map_size))
let perms = parse_segment_perm(program_header.flags);
let mut vm_map_options = root_vmar
.new_map(segment_size, perms)?
.vmo(segment_vmo)
.vmo_offset(segment_offset)
.vmo_limit(segment_offset + segment_size)
.can_overwrite(true);
let offset = base_addr + (program_header.virtual_addr as Vaddr).align_down(PAGE_SIZE);
vm_map_options = vm_map_options.offset(offset);
let map_addr = vm_map_options.build()?;
let anonymous_map_size: usize = if total_map_size > segment_size {
total_map_size - segment_size
} else {
0
};
if anonymous_map_size > 0 {
let mut anonymous_map_options = root_vmar
.new_map(anonymous_map_size, perms)?
.can_overwrite(true);
anonymous_map_options = anonymous_map_options.offset(offset + segment_size);
anonymous_map_options.build()?;
}
Ok(())
} }
fn parse_segment_perm(flags: xmas_elf::program::Flags) -> VmPerms { fn parse_segment_perm(flags: xmas_elf::program::Flags) -> VmPerms {
@ -411,15 +409,16 @@ pub fn init_aux_vec(elf: &Elf, elf_map_addr: Vaddr, ldso_base: Option<Vaddr>) ->
Ok(aux_vec) Ok(aux_vec)
} }
/// Map the vdso vmo to the corresponding virtual memory address. /// Maps the VDSO VMO to the corresponding virtual memory address.
fn map_vdso_to_vm(process_vm: &ProcessVm) -> Option<Vaddr> { fn map_vdso_to_vm(process_vm: &ProcessVm) -> Option<Vaddr> {
let root_vmar = process_vm.root_vmar(); let root_vmar = process_vm.root_vmar();
let vdso_vmo = vdso_vmo()?; let vdso_vmo = vdso_vmo()?;
let options = root_vmar let options = root_vmar
.new_map(vdso_vmo.dup().unwrap(), VmPerms::empty()) .new_map(VDSO_VMO_SIZE, VmPerms::empty())
.unwrap() .unwrap()
.size(5 * PAGE_SIZE); .vmo(vdso_vmo.dup().unwrap());
let vdso_data_base = options.build().unwrap(); let vdso_data_base = options.build().unwrap();
let vdso_text_base = vdso_data_base + 0x4000; let vdso_text_base = vdso_data_base + 0x4000;