kernel/executable/
mod.rs

1use kernel_user_link::process::ProcessMetadata;
2use tracing::trace;
3
4use crate::{cpu, fs, memory_management::virtual_memory_mapper};
5
6pub mod elf;
7
8/// # Safety
9/// The `vm` passed must be an exact kernel clone to the current vm
10/// without loading new process specific mappings
11pub unsafe fn load_elf_to_vm(
12    elf: &elf::Elf,
13    file: &mut fs::File,
14    process_meta: &mut ProcessMetadata,
15    vm: &mut virtual_memory_mapper::VirtualMemoryMapper,
16) -> Result<(usize, usize), fs::FileSystemError> {
17    // we can't be interrupted and load another process vm in the middle of this work
18    cpu::cpu().push_cli();
19    let old_vm = virtual_memory_mapper::get_current_vm();
20
21    // switch temporarily so we can map the elf
22    // SAFETY: this must be called while the current vm and this new vm must share the same
23    //         kernel regions
24    vm.switch_to_this();
25
26    let mut min_address = usize::MAX;
27    let mut max_address = 0;
28    let mut phdr_address = 0;
29
30    for segment in elf.program_headers() {
31        match segment.ty() {
32            elf::ElfProgramType::Load => {
33                let segment_virtual = segment.virtual_address();
34                assert_eq!(segment_virtual, segment.physical_address());
35
36                let mut flags = elf::to_virtual_memory_flags(segment.flags());
37                flags |= virtual_memory_mapper::flags::PTE_USER;
38                let entry = virtual_memory_mapper::VirtualMemoryMapEntry {
39                    virtual_address: segment_virtual as usize,
40                    physical_address: None,
41                    size: segment.mem_size() as usize,
42                    flags,
43                };
44                min_address = min_address.min(entry.virtual_address);
45                max_address = max_address.max(entry.virtual_address + entry.size);
46                trace!("Mapping segment: {:x?}", entry);
47                vm.map(&entry);
48
49                // read the file into the memory
50                file.seek(segment.offset())?;
51
52                let ptr = segment_virtual as *mut u8;
53                let slice =
54                    unsafe { core::slice::from_raw_parts_mut(ptr, segment.file_size() as usize) };
55
56                // read the whole segment
57                assert_eq!(file.read(slice)?, segment.file_size());
58            }
59            elf::ElfProgramType::ProgramHeader => {
60                phdr_address = segment.virtual_address() as usize;
61            }
62            _ => {}
63        }
64    }
65
66    for section in elf.sections() {
67        if section.name() == ".eh_frame" {
68            process_meta.eh_frame_address = section.address() as usize;
69            process_meta.eh_frame_size = section.size() as usize;
70        } else if section.name() == ".text" {
71            process_meta.text_address = section.address() as usize;
72            process_meta.text_size = section.size() as usize;
73        }
74    }
75
76    process_meta.image_base = min_address;
77    process_meta.image_size = max_address - min_address;
78    assert!(phdr_address >= min_address && phdr_address < max_address);
79    process_meta.program_headers_offset = phdr_address - min_address;
80
81    // reset if we got an invalid eh_frame, its optional
82    if process_meta.eh_frame_address < min_address || process_meta.eh_frame_address >= max_address {
83        process_meta.eh_frame_address = 0;
84        process_meta.eh_frame_size = 0;
85    }
86
87    // switch back to the old vm
88    old_vm.switch_to_this();
89    // we can be interrupted again
90    cpu::cpu().pop_cli();
91
92    Ok((min_address, max_address))
93}