1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
//! To parse the elf file and map it to the memory space
#![no_std]
pub mod arch;
extern crate alloc;
use alloc::vec::Vec;
use log::info;
use memory_addr::{VirtAddr, PAGE_SIZE_4K};
use page_table_entry::MappingFlags;
mod auxv;
pub use auxv::{get_app_stack_region, get_auxv_vector};
mod user_stack;
pub use crate::arch::get_relocate_pairs;
/// The segment of the elf file, which is used to map the elf file to the memory space
pub struct ELFSegment {
/// The start virtual address of the segment
pub vaddr: VirtAddr,
/// The size of the segment
pub size: usize,
/// The flags of the segment which is used to set the page table entry
pub flags: MappingFlags,
/// The data of the segment
pub data: Option<Vec<u8>>,
}
/// To parse the elf file and return the segments of the elf file
///
/// # Arguments
///
/// * `elf_data` - The elf file data
/// * `elf_base_addr` - The base address of the elf file if the file will be loaded to the memory
///
/// # Return
/// Return the entry point, the segments of the elf file and the relocate pairs
///
/// # Warning
/// It can't be used to parse the elf file which need the dynamic linker, but you can do this by calling this function recursively
pub fn get_elf_segments(elf: &xmas_elf::ElfFile, elf_base_addr: Option<usize>) -> Vec<ELFSegment> {
let elf_header = elf.header;
let magic = elf_header.pt1.magic;
assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!");
// Some elf will load ELF Header (offset == 0) to vaddr 0. In that case, base_addr will be added to all the LOAD.
let base_addr = if let Some(header) = elf
.program_iter()
.find(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Load))
{
// Loading ELF Header into memory.
let vaddr = header.virtual_addr() as usize;
if vaddr == 0 {
if let Some(addr) = elf_base_addr {
addr
} else {
panic!("ELF Header is loaded to vaddr 0, but no base_addr is provided");
}
} else {
0
}
} else {
0
};
info!("Base addr for the elf: 0x{:x}", base_addr);
let mut segments = Vec::new();
// Load Elf "LOAD" segments at base_addr.
elf.program_iter()
.filter(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Load))
.for_each(|ph| {
let mut start_va = ph.virtual_addr() as usize + base_addr;
let end_va = (ph.virtual_addr() + ph.mem_size()) as usize + base_addr;
let mut start_offset = ph.offset() as usize;
let end_offset = (ph.offset() + ph.file_size()) as usize;
// Virtual address from elf may not be aligned.
assert_eq!(start_va % PAGE_SIZE_4K, start_offset % PAGE_SIZE_4K);
let front_pad = start_va % PAGE_SIZE_4K;
start_va -= front_pad;
start_offset -= front_pad;
let mut flags = MappingFlags::USER;
if ph.flags().is_read() {
flags |= MappingFlags::READ;
}
if ph.flags().is_write() {
flags |= MappingFlags::WRITE;
}
if ph.flags().is_execute() {
flags |= MappingFlags::EXECUTE;
}
let data = Some(elf.input[start_offset..end_offset].to_vec());
segments.push(ELFSegment {
vaddr: VirtAddr::from(start_va),
size: end_va - start_va,
flags,
data,
});
});
segments
}
/// To parse the elf file and return the segments of the elf file
///
/// # Arguments
///
/// * `elf_data` - The elf file data
/// * `elf_base_addr` - The base address of the elf file if the file will be loaded to the memory
///
/// # Return
/// Return the entry point
///
/// # Warning
/// It can't be used to parse the elf file which need the dynamic linker, but you can do this by calling this function recursively
pub fn get_elf_entry(elf: &xmas_elf::ElfFile, elf_base_addr: Option<usize>) -> VirtAddr {
let elf_header = elf.header;
let magic = elf_header.pt1.magic;
assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!");
// Some elf will load ELF Header (offset == 0) to vaddr 0. In that case, base_addr will be added to all the LOAD.
let base_addr = if let Some(header) = elf
.program_iter()
.find(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Load))
{
// Loading ELF Header into memory.
let vaddr = header.virtual_addr() as usize;
if vaddr == 0 {
if let Some(addr) = elf_base_addr {
addr
} else {
panic!("ELF Header is loaded to vaddr 0, but no base_addr is provided");
}
} else {
0
}
} else {
0
};
info!("Base addr for the elf: 0x{:x}", base_addr);
let entry = elf.header.pt2.entry_point() as usize + base_addr;
entry.into()
}